Skip to content

fix(daemon): write daemon status via sync write_atomic (#865)#884

Merged
zackees merged 1 commit into
mainfrom
fix/865-status-write-current-thread-runtime
Jun 30, 2026
Merged

fix(daemon): write daemon status via sync write_atomic (#865)#884
zackees merged 1 commit into
mainfrom
fix/865-status-write-current-thread-runtime

Conversation

@zackees

@zackees zackees commented Jun 30, 2026

Copy link
Copy Markdown
Member

Problem

StatusManager::write_atomic bridged to the async fbuild_core::fs::write_atomic via tokio::task::block_in_place + Handle::block_on. block_in_place panics inside a current-thread tokio runtime — the default flavor of #[tokio::test] — so every unit test that constructed a DaemonContext (which constructs a StatusManager, whose new calls flush(), which calls write_atomic) panicked on macOS + Windows CI. Three named tests were affected:

  • handlers::health::tests::shutdown_refuses_non_force_when_operation_in_progress
  • handlers::locks::tests::lock_status_reports_held_project_locks_without_stale_entries
  • handlers::locks::tests::clear_locks_removes_only_unheld_project_lock_entries

Fix

Add fbuild_core::fs::write_atomic_sync — same write-tempfile / fsync / rename semantics as the async helper, but std::fs end-to-end so it has no runtime-flavor requirement. Switch StatusManager::write_atomic to call it. The status file's atomicity contract is preserved verbatim; the only behavior change is that the call is no longer runtime-aware.

Why sync instead of runtime-flavor sniffing

The fix sketch in the issue proposed Handle::current().runtime_flavor() matching to route current-thread runtimes to a spawned tokio::runtime::Runtime for the block_on. That nests runtimes (also disallowed) and adds a per-write Runtime::new() allocation. A sync std::fs helper sidesteps both issues — and the status writer was already inside synchronous code paths inside a sync-locked Mutex<DaemonStatus>, so there's no async-blocking concern.

Tests

Two regression tests in fs::tests:

  • write_atomic_sync_runs_inside_current_thread_runtime — exact panic scenario, asserts no panic + correct content
  • write_atomic_sync_works_without_runtime — cold-bootstrap path

Verification

  • soldr cargo check -p fbuild-core -p fbuild-daemon clean
  • soldr cargo clippy -p fbuild-core -p fbuild-daemon --all-targets -- -D warnings clean
  • soldr cargo test -p fbuild-core --lib fs::tests::write_atomic_sync_ clean

Closes #865

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a more reliable atomic file-writing path for synchronous operations.
  • Bug Fixes

    • Improved file writes in daemon status handling to avoid runtime-related panics in certain environments.
    • Added safeguards so atomic writes fail cleanly when the target folder does not exist.
    • Expanded test coverage for atomic writes across common runtime and non-runtime scenarios.

`StatusManager::write_atomic` bridged to the async
`fbuild_core::fs::write_atomic` via `tokio::task::block_in_place` +
`Handle::block_on`. `block_in_place` panics inside a current-thread
tokio runtime — which is the default flavor of `#[tokio::test]` — so
every unit test that constructed a `DaemonContext` (which constructs
a `StatusManager`, whose `new` calls `flush()`, which calls
`write_atomic`) panicked on macOS + Windows CI. Three named tests are
affected:

- handlers::health::tests::shutdown_refuses_non_force_when_operation_in_progress
- handlers::locks::tests::lock_status_reports_held_project_locks_without_stale_entries
- handlers::locks::tests::clear_locks_removes_only_unheld_project_lock_entries

Fix: add `fbuild_core::fs::write_atomic_sync` — same write-tempfile /
fsync / rename semantics as the async helper, but `std::fs` end-to-end
so it has no runtime-flavor requirement. Switch `StatusManager::write_atomic`
to call it. The status file's atomicity contract is preserved
verbatim; the only behavior change is that the call is no longer
runtime-aware.

Two new regression tests in `fs::tests`:
- `write_atomic_sync_runs_inside_current_thread_runtime` (the exact
  scenario that was panicking)
- `write_atomic_sync_works_without_runtime` (cold-bootstrap path)

Closes #865
@zackees zackees merged commit b9d1efb into main Jun 30, 2026
81 of 93 checks passed
@zackees zackees deleted the fix/865-status-write-current-thread-runtime branch June 30, 2026 18:02
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3c7ead93-ddcb-48dc-9145-6c312b781665

📥 Commits

Reviewing files that changed from the base of the PR and between e8fcffb and cb9635e.

📒 Files selected for processing (2)
  • crates/fbuild-core/src/fs.rs
  • crates/fbuild-daemon/src/status_manager.rs

📝 Walkthrough

Walkthrough

Adds write_atomic_sync, a synchronous atomic file writer in fbuild-core/src/fs.rs, mirroring the existing async variant. StatusManager::write_atomic in fbuild-daemon is then updated to call this new function directly, removing the previous Tokio block_in_place/block_on bridging logic.

Sync atomic write + StatusManager migration

Layer / File(s) Summary
write_atomic_sync implementation and tests
crates/fbuild-core/src/fs.rs
Adds the synchronous atomic-write function (PID+nonce temp sibling, parent-exists check, write + sync_all, rename, cleanup on error) and extends tests with a current-thread Tokio runtime regression case, a no-runtime case, and a missing-parent-directory error case.
StatusManager migration
crates/fbuild-daemon/src/status_manager.rs
Replaces the Tokio block_in_place/block_on bridging (with runtime-creation fallback) in StatusManager::write_atomic with a direct call to fbuild_core::fs::write_atomic_sync, and updates the inline docs to match.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Poem

🐇 A temp file appears, then renames in place,
No Tokio bridging, no panic to face.
The nonce and the PID make siblings unique,
sync_all ensures nothing goes weak.
The rabbit hops free — no runtime to chase!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/865-status-write-current-thread-runtime

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

zackees added a commit that referenced this pull request Jun 30, 2026
Rolls up the fixes shipped since 2.3.14:

- #855 (#883) — drop stale standalone-zccache CI/Docker/docs references
  after the soldr embedded-zccache transition (soldr#977/#980/#1081)
- #865 (#884) — write daemon status via sync write_atomic; unblocks
  fbuild-daemon unit tests on macOS/Windows by eliminating the
  block_in_place panic in current-thread tokio runtimes
- #875 (#885) — pin TMP/TEMP for compiler subprocess on Windows; ESP32
  compile no longer fails with 'Cannot create temporary file in
  C:\Windows\' on the very first TU
- #720 (#886) — record anti-removal policy for dump_usb_ids example
- #829 (#887) — stage fbuild._native cdylib during source/editable
  install so 'from fbuild import ...' works after 'uv pip install -e .'

Plus all prior #664 platform_packages audit work, the #826 testing
followups, and the build cancellation fix from earlier in the cycle.
@fastled-project-sync fastled-project-sync Bot moved this to Triage in FastLED Tracker Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

fbuild-daemon: status_manager.rs:221 block_in_place panics in current-thread runtime tests

1 participant